package timing.ukulele.auth.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.web.filter.CorsFilter;
import timing.ukulele.auth.config.properties.TimingSecurityProperties;
import timing.ukulele.auth.security.filter.JWTSecurityFilter;
import timing.ukulele.auth.security.type.image.ImageCodeAuthenticationSecurityConfig;
import timing.ukulele.auth.security.type.sms.SmsCodeAuthenticationSecurityConfig;
import timing.ukulele.auth.security.type.wechat.ThirdQrAuthenticationSecurityConfig;
import timing.ukulele.auth.support.RedisOperator;

/**
 * 安全配置
 * <p>
 * {@link EnableMethodSecurity} 开启全局方法认证，启用JSR250注解支持，启用注解 {@link Secured} 支持，
 * 在Spring Security 6.0版本中将@Configuration注解从@EnableWebSecurity, @EnableMethodSecurity, @EnableGlobalMethodSecurity
 * 和 @EnableGlobalAuthentication 中移除，使用这些注解需手动添加 @Configuration 注解
 * {@link EnableWebSecurity} 注解有两个作用:
 * 1. 加载了WebSecurityConfiguration配置类, 配置安全认证策略。
 * 2. 加载了AuthenticationConfiguration, 配置了认证信息。
 */
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(jsr250Enabled = true, securedEnabled = true)
public class SecurityConfig {
    private final RedisOperator<String> redisOperator;
    private final TimingSecurityProperties customSecurityProperties;
    private final SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
    private final ImageCodeAuthenticationSecurityConfig imageCodeAuthenticationSecurityConfig;
    private final ThirdQrAuthenticationSecurityConfig thirdQrAuthenticationSecurityConfig;

    public SecurityConfig(
            RedisOperator<String> redisOperator,
            TimingSecurityProperties customSecurityProperties,
            SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig,
            ImageCodeAuthenticationSecurityConfig imageCodeAuthenticationSecurityConfig,
            ThirdQrAuthenticationSecurityConfig thirdQrAuthenticationSecurityConfig) {
        this.redisOperator = redisOperator;
        this.customSecurityProperties = customSecurityProperties;
        this.smsCodeAuthenticationSecurityConfig = smsCodeAuthenticationSecurityConfig;
        this.imageCodeAuthenticationSecurityConfig = imageCodeAuthenticationSecurityConfig;
        this.thirdQrAuthenticationSecurityConfig = thirdQrAuthenticationSecurityConfig;
    }

    /**
     * 配置认证相关的过滤器链(资源服务，客户端配置)
     *
     * @param http spring security核心配置类
     * @return 过滤器链
     * @throws Exception 抛出
     */
    @Bean
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http,
                                                          CorsFilter corsFilter,
                                                          JwtDecoder jwtDecoder,
                                                          LogoutSuccessHandler logoutSuccessHandler) throws Exception {
        // 添加基础的认证配置
        http.addFilter(corsFilter);
        // 禁用cors
//        http.cors(AbstractHttpConfigurer::disable);

        // 禁用 csrf
        http.csrf(AbstractHttpConfigurer::disable);

        http.httpBasic(Customizer.withDefaults())
                .authorizeHttpRequests((authorize) -> authorize
                        // 放行静态资源和不需要认证的url
                        .requestMatchers(customSecurityProperties.getIgnoreUriList().toArray(new String[0])).permitAll()
                        .anyRequest().authenticated()
                )
                .formLogin(AbstractHttpConfigurer::disable)
                .logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler));

        http.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        http.apply(smsCodeAuthenticationSecurityConfig);
        http.apply(imageCodeAuthenticationSecurityConfig);
        http.apply(thirdQrAuthenticationSecurityConfig);

        JWTSecurityFilter jwtFilter = new JWTSecurityFilter(jwtDecoder, redisOperator);
        http.addFilterAt(jwtFilter, UsernamePasswordAuthenticationFilter.class);

        DefaultSecurityFilterChain build = http.build();

        return build;
    }

}
